using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using Microsoft.Win32;
using SharpMap.Layers;
using SharpMap.Presentation;
using SharpMap.Presentation.Views;
using SharpMap.Presentation.Wpf;
namespace SharpMap.Wpf
{
    /// <summary>
    /// Presents list of layers in the Map which can be set explicity through
    /// the Map property,
    /// or through DataContext
    /// </summary>
    public class LayersListView : ListView, ILayersView
    //TODO: the Command handeling should be moved to the LayerListViewModel
    {

        static LayersListView()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(LayersListView), new FrameworkPropertyMetadata(typeof(LayersListView)));
            DataContextProperty.OverrideMetadata(typeof(LayersListView),
                new FrameworkPropertyMetadata(DataContextPropertyChanged));
        }



        static void DataContextPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            LayersListView This = target as LayersListView;
            if (This != null)
            {
                LayerListViewModel llvm = e.NewValue as LayerListViewModel;
                if (llvm != null)
                {
                    return;
                }
                SharpMap.Map map = e.NewValue as SharpMap.Map;
                if(map != null)
                {
                    This.Map = map; //there will be created LayerListViewModel
                                    //and will be set as the data context
                }
            }
        }



        public LayersListView()
        {
            WireUpBindings();
            WireUpCommandBinding();
        }

        private void WireUpCommandBinding()
        {
            CommandBinding cb = new CommandBinding(MapViewCommands.ShowOpenLayerDialog,
                OpenLayerHadler, canExecuteOpenDialog);
            CommandBindings.Add(cb);

           cb = new CommandBinding(LayerCommands.BringForward,moveUpHandler,canExecuteBringFarward);
           CommandBindings.Add(cb);

           cb = new CommandBinding(LayerCommands.SendBackward, moveDownHandler, canExecuteSendBackward);
           CommandBindings.Add(cb);
        }

        private void WireUpBindings()
        {
            Binding b = new Binding("Layers");
            b.Mode = BindingMode.OneWay;
            SetBinding(ItemsSourceProperty, b);

            //b = new Binding("Map");
            //b.Mode = BindingMode.OneWay;
            //SetBinding(MapProperty, b);
        }
    
        public static readonly DependencyProperty MapProperty =
            DependencyProperty.Register("Map", typeof(SharpMap.Map), typeof(LayersListView)
            , new FrameworkPropertyMetadata(default(SharpMap.Map),mapPropertyChanged));
        
        public SharpMap.Map Map
        {
            get { return (SharpMap.Map)GetValue(MapProperty); }
            set { SetValue(MapProperty, value); }
        }

        static void mapPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            LayersListView This = target as LayersListView;
            SharpMap.Map map = e.NewValue as SharpMap.Map;
            if (This != null && map != null)
            {
                This._presenter = new LayerListViewModel(map, This);
                This.DataContext = This._presenter;
            }
        }


        protected override void OnSelectionChanged(SelectionChangedEventArgs e)
        {
            base.OnSelectionChanged(e);
            if (e.AddedItems.Count != 0)
            {
                var layerNames = from SharpMap.Layers.ILayer item in e.AddedItems
                                 select item.LayerName;
                onRequestLayersSelectionChanged(layerNames, LayerAction.Selected);
            }
            if (e.RemovedItems.Count != 0)
            {
                var layerNames = from SharpMap.Layers.ILayer item in e.RemovedItems
                                 select item.LayerName;
                onRequestLayersSelectionChanged(layerNames, LayerAction.Deselected);
            }
           
        }
  
        #region ILayersView Members

        IBindingList ILayersView.Layers
        {
            set { throw new NotImplementedException(); }
        }

        IList<String> ILayersView.SelectedLayers
        {
            get { throw new NotImplementedException(); }
            set { throw new NotImplementedException(); }

        }

        public void DisableLayer(String layer)
        {
            onRequestLayerEnableChanged(layer, LayerAction.Disabled);
        }

        public void EnableLayer(String layer)
        {
            onRequestLayerEnableChanged(layer, LayerAction.Enabled);
        }



        public event EventHandler<LayerActionEventArgs> LayersSelectionChangeRequested;

        public event EventHandler<LayerActionEventArgs> LayersEnabledChangeRequested;

        #endregion

        #region IView Members

        public System.Boolean Visible
        {
            get
            {
                return this.Visibility == Visibility.Visible;
            }
            set
            {
                this.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
            }
        }

        public System.Boolean Enabled
        {
            get
            {
                return IsEnabled;
            }
            set
            {
                IsEnabled = value;
            }
        }

        public void Hide()
        {
            Visibility = Visibility.Collapsed;
        }

        public void Show()
        {
            Visibility = Visibility.Visible;
        }

        public String Title
        {
            get
            {
                throw new Exception("The method or operation is not implemented.");
            }
            set
            {
                throw new Exception("The method or operation is not implemented.");
            }
        }

        #endregion

        #region event invokers
        void onRequestLayersSelectionChanged(IEnumerable<string> layers, LayerAction action)
        {
            EventHandler<LayerActionEventArgs> handler = LayersSelectionChangeRequested;
            if (handler != null)
            {
                LayerActionEventArgs args = new LayerActionEventArgs(layers, action);
                handler(this, args);   
            }
        }

        void onRequestLayerEnableChanged(string name, LayerAction action)
        {
            EventHandler<LayerActionEventArgs> handler = LayersEnabledChangeRequested;
            if (handler != null)
            {
                LayerActionEventArgs args = new LayerActionEventArgs(name, action);
                handler(this, args);
            }
        }
        #endregion


        void OpenLayerHadler(object sender, RoutedEventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();

            dlg.DefaultExt = ".shp";
            dlg.Filter = "Shape Files (.shp)|*.shp";
            dlg.Multiselect = true;

            Nullable<System.Boolean> result = dlg.ShowDialog();
            foreach (String fileName in dlg.FileNames)
            {
                GeometryLayer l = LoadLayer(fileName, null);
                l.DataSource.Open();
                Map.AddLayer(l);

            }
        }

        void RemoveLayerHandler(object sender, RoutedEventArgs e)
        {
            if (SelectedLayer != null)
            {
                //m_mapControl.Map.RemoveLayer((ILayer)_layerList.SelectedItem);
                SelectedLayer.DataSource.Close();
                Map.Layers.Remove(SelectedLayer);
            }
        }

        void moveUpHandler(object sender, RoutedEventArgs e)
        {
         
            
            if (SelectedLayer != null)
            {
                int selectedIndex = Map.Layers.IndexOf(SelectedLayer);
                ILayer layerToMove = SelectedLayer;
                //m_mapControl.Map.RemoveLayer((ILayer)_layerList.SelectedItem);
                //((ILayer)_layerList.SelectedItem).DataSource.Close();
                Map.Layers.RaiseListChangedEvents = false;
                Map.Layers.Remove(layerToMove);
                Map.Layers.Insert(selectedIndex - 1, layerToMove);
                //_layerList.SelectedItem = selectedIndex - 1;
                Map.Layers.RaiseListChangedEvents = true;
                Map.Layers.ResetBindings();

            }
        }

        void moveDownHandler(object sender, RoutedEventArgs e)
        {
            //////////////////////////////////////////////

            if (SelectedLayer != null)
            {
                int selectedIndex = Map.Layers.IndexOf(SelectedLayer);
                ILayer layerToMove = SelectedLayer;
                //int rowIndex = (ILayer) _layerList.SelectedItems[0].
                Map.DeselectLayers(new int[] { selectedIndex });
                Map.RemoveLayer(layerToMove);
                //((ILayer)_layerList.SelectedItem).DataSource.Close();
                if (selectedIndex < Map.Layers.Count)
                {
                    Map.Layers.Insert(selectedIndex + 1, layerToMove);
                }
                else
                {
                    Map.Layers.Add(layerToMove);
                }
                //_layerList.SelectedItem = (selectedIndex + 1);
            }
        }


        private SharpMap.Layers.GeometryLayer LoadLayer(String fileName, string givenLayerName)
        {
            SharpMap.Data.Providers.ShapeFile.ShapeFileProvider shapFile =
            new SharpMap.Data.Providers.ShapeFile.ShapeFileProvider(fileName);

            String layerName = givenLayerName ?? fileName.Substring(fileName.LastIndexOf('\\') + 1);

            SharpMap.Layers.GeometryLayer layer = new SharpMap.Layers.GeometryLayer(layerName, shapFile);
            return layer;
        }

        void canExecuteOpenDialog(Object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute =   Map != null;
        }

        void canExecuteRemove(Object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (SelectedItem != null && Items.Count >= 1); 
        }

        void canExecuteBringFarward(Object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (SelectedItem != null && 
                            SelectedIndex > 0); 
        }

        void canExecuteSendBackward(Object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = (SelectedItem != null &&
                           SelectedIndex < Items.Count - 1); 
        }


        ILayer SelectedLayer
        {
            get { return SelectedItem as ILayer; }
        }

        #region fields
        private LayerListViewModel _presenter;
        
        #endregion //fields
    }
}
